﻿#if UNITY_EDITOR

using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;

namespace AI.Graph.Editor {


    public static class EditorGUI {

        private const int m_notStartedToggleIndexInGroup = -123;
        private static int m_currentToggleIndexInGroup;
        private static int m_selectedToggleIndexInGroup;
        private static Vector2 m_currentMousePosition;
        private static int m_isButtonDownControlID;
        private static int m_isToggleDownControlID;
        private static Texture2D m_solidWhite;

        public static readonly int defaultTextSize = 10;
        public static readonly float defaultTextureAspect = 0;
        public static readonly bool defaultAlphaBlend = true;

        public static readonly GUIStyle toggleLabelStyle;
        public static readonly GUIStyle buttonLabelStyle;
        public static readonly GUIStyle textStyleCenter;
        public static readonly GUIStyle textStyleRight;
        public static readonly GUIStyle textStyleLeft;
        public static readonly GUIStyle textStyleBoldCenter;
        public static readonly GUIStyle textStyleBoldRight;
        public static readonly GUIStyle textStyleBoldLeft;

        public static readonly EditorColor toolbarColor = new EditorColor (new Color32 (218, 218, 218, 255), new Color32 (45, 45, 45, 255));
        public static readonly EditorColor toolbarOutlineColor = new EditorColor (new Color32 (150, 150, 150, 255), new Color32 (29, 29, 29, 255));
        public static readonly EditorColor toggleActiveColor = new EditorColor (new Color32 (170, 170, 170, 255), new Color32 (75, 75, 75, 255));
        public static readonly EditorColor toggleHoverColor = new EditorColor (new Color32 (170, 170, 170, 255), new Color32 (60, 60, 60, 255));
        public static readonly EditorColor toggleHighlightColor = new EditorColor (new Color32 (70, 114, 176, 255), new Color32 (70, 114, 176, 255));
        public static readonly EditorColor toggleHighlightDisabledColor = new EditorColor (new Color32 (70, 114, 176, (int) (255 * 0.65F)), new Color32 (70, 114, 176, (int) (255 * 0.65F)));
        public static readonly EditorColor buttonHoverColor = new EditorColor (new Color32 (170, 170, 170, 255), new Color32 (60, 60, 60, 255));
        public static readonly EditorColor buttonColor = new EditorColor (new Color32 (0, 0, 0, 0), new Color32 (0, 0, 0, 0));
        public static readonly EditorColor textColor = new EditorColor (new Color32 (0, 0, 0, 255), new Color32 (181, 181, 181, 255));

        public static readonly EditorColor pingFieldBackgroundColor = new EditorColor (new Color32 (190, 190, 190, 255), new Color32 (75, 75, 75, 255));
        public static readonly EditorColor pingFieldOutlineColor = new EditorColor (new Color32 (150, 150, 150, 255), new Color32 (29, 29, 29, 255));

        public static Texture2D solidWhite {
            get {
                if (m_solidWhite == null) {
                    m_solidWhite = new Texture2D (1, 1);
                    m_solidWhite.SetPixel (0, 0, Color.white);
                    m_solidWhite.SetPixel (1, 0, Color.white);
                    m_solidWhite.SetPixel (0, 1, Color.white);
                    m_solidWhite.SetPixel (1, 1, Color.white);
                    m_solidWhite.Apply ();
                }

                return m_solidWhite;
            }
        }


        static EditorGUI () {
            toggleLabelStyle = new GUIStyle (GUI.skin.label);
            toggleLabelStyle.alignment = TextAnchor.MiddleCenter;
            toggleLabelStyle.fontSize = defaultTextSize;

            buttonLabelStyle = new GUIStyle (GUI.skin.label);
            buttonLabelStyle.alignment = TextAnchor.MiddleCenter;
            buttonLabelStyle.fontSize = defaultTextSize;

            textStyleCenter = new GUIStyle (GUI.skin.label);
            textStyleCenter.alignment = TextAnchor.MiddleCenter;
            textStyleCenter.fontSize = defaultTextSize;

            textStyleRight = new GUIStyle (GUI.skin.label);
            textStyleRight.alignment = TextAnchor.MiddleRight;
            textStyleRight.fontSize = defaultTextSize;

            textStyleLeft = new GUIStyle (GUI.skin.label);
            textStyleLeft.alignment = TextAnchor.MiddleLeft;

            textStyleBoldCenter = new GUIStyle (textStyleCenter);
            textStyleBoldCenter.fontStyle = FontStyle.Bold;

            textStyleBoldRight = new GUIStyle (textStyleRight);
            textStyleBoldRight.fontStyle = FontStyle.Bold;

            textStyleBoldLeft = new GUIStyle (textStyleLeft);
            textStyleBoldLeft.fontStyle = FontStyle.Bold;

            SetGUIStyleColors ();
            
            AIGraphPreferences.onSkinChanged += skin => {
                SetGUIStyleColors ();
            };
        }


        static void SetGUIStyleColors () {
            toggleLabelStyle.normal.textColor = textColor.color;
            buttonLabelStyle.normal.textColor = textColor.color;
            textStyleCenter.normal.textColor = textColor.color;
            textStyleRight.normal.textColor = textColor.color;
            textStyleLeft.normal.textColor = textColor.color;
        }


        public static float HorizontalScaleSlider (Vector2 pos, float width, float height) {
            Rect cursorRect = new Rect (pos.x - width / 2F, pos.y, width, height);
            return ScaleSlider ("AI.Graph.Editor.EditorGUI.HorizontalScaleSlider", pos, MouseCursor.ResizeHorizontal, cursorRect).x;
        }


        public static float VerticalScaleSlider (Vector2 pos, float width, float height) {
            Rect cursorRect = new Rect (pos.x, pos.y - height / 2F, width, height);
            return ScaleSlider ("AI.Graph.Editor.EditorGUI.VerticalScaleSlider", pos, MouseCursor.ResizeVertical, cursorRect).y;
        }


        static Vector2 ScaleSlider (string sliderName, Vector2 pos, MouseCursor cursor, Rect cursorRect) {
            int controlId = GUIUtility.GetControlID (sliderName.GetHashCode (), FocusType.Passive);
            return ScaleSlider (controlId, pos, cursor, cursorRect);
        }


        static Vector2 ScaleSlider (int id, Vector2 pos, MouseCursor cursor, Rect cursorRect) {
            Event e = Event.current;
            switch (e.GetTypeForControl (id)) {
                case EventType.MouseDown:
                    if (e.button == 0 && cursorRect.Contains (Event.current.mousePosition) && !e.alt) {
                        GUIUtility.keyboardControl = id;
                        GUIUtility.hotControl = id;
                        m_currentMousePosition = e.mousePosition;
                        e.Use ();
                        UnityEditor.EditorGUIUtility.SetWantsMouseJumping (1);
                    }

                    break;

                case EventType.MouseUp:
                    if (GUIUtility.hotControl == id && e.button == 0) {
                        GUIUtility.hotControl = 0;
                        e.Use ();
                        UnityEditor.EditorGUIUtility.SetWantsMouseJumping (0);
                    }

                    break;

                case EventType.MouseDrag:
                    if (GUIUtility.hotControl == id) {
                        m_currentMousePosition += e.delta;
                        if (!Mathf.Approximately ((pos - m_currentMousePosition).magnitude, 0.0f)) {
                            GUI.changed = true;
                        }

                        pos = m_currentMousePosition;
                        e.Use ();
                        return pos;
                    }

                    break;

                case EventType.Repaint:
                    UnityEditor.EditorGUIUtility.AddCursorRect (cursorRect, cursor, id);
                    break;
            }

            return pos;
        }


        public static void Toolbar (Rect rect, bool borderLeft = true, bool borderRight = true, bool borderTop = true, bool borderBottom = true) {
            Toolbar (rect, toolbarColor.color, toolbarOutlineColor.color, borderLeft, borderRight, borderTop, borderBottom);
        }


        public static void Toolbar (Rect rect, Color toolbarColor, Color toolbarOutlineColor, bool borderLeft = true, bool borderRight = true, bool borderTop = true, bool borderBottom = true) {
            DrawSolidTexture (rect, toolbarColor);
            float width = 1F;
            float l = borderLeft ? width : 0;
            float r = borderRight ? width : 0;
            float t = borderTop ? width : 0;
            float b = borderBottom ? width : 0;

            if (l + r + t + b > 0) {
                DrawSolidTexture (rect, toolbarOutlineColor, new BorderWidth (l, r, t, b));
            }
        }


        public static bool Button (Rect rect, string label, GUIStyle labelStyle = null) {
            return Button (rect, label, buttonColor.color, buttonHoverColor.color, labelStyle);
        }


        public static bool Button (Rect rect, string label, Color hoverColor, GUIStyle labelStyle = null) {
            return Button (rect, label, buttonColor.color, hoverColor, labelStyle);
        }


        public static bool Button (Rect rect, string label, Color buttonColor, Color hoverColor, GUIStyle labelStyle = null) {
            rect.x += 1;
            rect.y += 1;
            rect.width -= 2;
            rect.height -= 2;
            Event e = Event.current;

            bool clicked = false;

            int id = GUIUtility.GetControlID ("AI.Graph.Editor.EditorGUI.Button".GetHashCode (), FocusType.Passive, rect);

            switch (e.GetTypeForControl (id)) {
                case EventType.MouseDown:
                    if (e.button == (int) MouseButton.Left && e.IsMouseInsideRect (rect)) {
                        GUIUtility.keyboardControl = id;
                        GUIUtility.hotControl = id;
                        m_isButtonDownControlID = id;

                        e.Use ();
                        UnityEditor.EditorGUIUtility.SetWantsMouseJumping (1);
                    }

                    break;

                case EventType.MouseUp:
                    if (GUIUtility.hotControl == id && e.button == 0) {
                        GUIUtility.hotControl = 0;
                        m_isButtonDownControlID = 0;
                        clicked = true;
                        e.Use ();
                        UnityEditor.EditorGUIUtility.SetWantsMouseJumping (0);
                    }

                    break;

                case EventType.MouseDrag:
                    if (GUIUtility.hotControl == id) {
                        e.Use ();
                    }

                    break;

                case EventType.Repaint:


                    if (e.IsMouseInsideRect (rect) && m_isButtonDownControlID == 0 && m_isToggleDownControlID == 0) {
                        // hover
                        if (GUI.enabled && hoverColor.a > 0) {
                            DrawSolidTexture (rect, hoverColor);
                        }
                    }
                    else if (id != m_isButtonDownControlID) {
                        // normal
                        if (GUI.enabled) {
                            if (buttonColor.a > 0) {
                                DrawSolidTexture (rect, buttonColor);
                            }
                        }
                        else {
                            if (buttonColor.a > 0) {
                                float r = buttonColor.r;
                                float g = buttonColor.r;
                                float b = buttonColor.r;
                                float a = buttonColor.a * 0.7F;
                                DrawSolidTexture (rect, new Color (r, g, b, a));
                            }
                        }
                    }
                    else {
                        if (hoverColor.a > 0) {
                            DrawSolidTexture (rect, hoverColor);
                        }

                        DrawSolidTexture (rect, toggleHighlightColor.color, new BorderWidth (1));
                    }

                    GUI.Label (rect, label, labelStyle ?? buttonLabelStyle);

                    break;
            }

            return clicked;
        }


        public static bool Toggle (Rect rect, bool value, string label, GUIStyle labelStyle = null) {
            return Toggle (rect, value, label, toggleActiveColor.color, toggleHoverColor.color, labelStyle);
        }


        public static bool Toggle (Rect rect, bool value, string label, Color activeColor, Color hoverColor, GUIStyle labelStyle = null) {
            rect.x += 1;
            rect.y += 1;
            rect.width -= 2;
            rect.height -= 2;
            Event e = Event.current;


            int id = GUIUtility.GetControlID ("AI.Graph.Editor.EditorGUI.Toggle".GetHashCode (), FocusType.Passive, rect);

            switch (e.GetTypeForControl (id)) {
                case EventType.MouseDown:
                    if (e.button == (int) MouseButton.Left && e.IsMouseInsideRect (rect)) {
                        GUIUtility.keyboardControl = id;
                        GUIUtility.hotControl = id;
                        m_isToggleDownControlID = id;
                        value = !value;
                        e.Use ();
                        UnityEditor.EditorGUIUtility.SetWantsMouseJumping (1);
                    }

                    break;

                case EventType.MouseUp:
                    if (GUIUtility.hotControl == id && e.button == 0) {
                        GUIUtility.hotControl = 0;
                        m_isToggleDownControlID = 0;
                        e.Use ();
                        UnityEditor.EditorGUIUtility.SetWantsMouseJumping (0);
                    }

                    break;

                case EventType.MouseDrag:
                    if (GUIUtility.hotControl == id) {
                        e.Use ();
                    }

                    break;

                case EventType.Repaint:

                    if (value) {
                        if (GUI.enabled) {
                            DrawSolidTexture (rect, activeColor);
                            DrawSolidTexture (new Rect (rect.x, rect.y - 1, rect.width, 2), toggleHighlightColor.color);
                        }
                        else {
                            activeColor.a *= 0.7F;
                            DrawSolidTexture (rect, activeColor);
                            DrawSolidTexture (new Rect (rect.x, rect.y - 1, rect.width, 2), toggleHighlightDisabledColor.color);
                        }
                    }
                    else if (e.IsMouseInsideRect (rect) && GUI.enabled && (m_isToggleDownControlID == 0 || m_isToggleDownControlID == id) && m_isButtonDownControlID == 0) {
                        DrawSolidTexture (rect, hoverColor);

                        // todo maybe ??
//                      NodeGUI.DrawSolidTexture (new Rect(rect.x, rect.y - 1, rect.width, 2), activeColor);
                    }

                    GUI.Label (rect, label, labelStyle ?? toggleLabelStyle);

                    break;
            }

            return value;
        }


        public static void ToggleForGroup (Rect rect, string label, GUIStyle labelStyle = null) {
            ToggleForGroup (rect, label, toggleActiveColor.color, toggleHoverColor.color, labelStyle);
        }


        public static void ToggleForGroup (Rect rect, string label, Color activeColor, Color hoverColor, GUIStyle labelStyle = null) {
            if (m_selectedToggleIndexInGroup == m_notStartedToggleIndexInGroup) {
                Debug.LogError ("No Toggle group started.");
                return;
            }

            bool selected = m_currentToggleIndexInGroup == m_selectedToggleIndexInGroup;

            if (selected && !GUI.enabled) {
                m_selectedToggleIndexInGroup = -1;
                selected = false;
            }

            bool newSelected = Toggle (rect, selected, label, activeColor, hoverColor, labelStyle);

            if (newSelected != selected) {
                m_selectedToggleIndexInGroup = newSelected ? m_currentToggleIndexInGroup : -1;
            }

            m_currentToggleIndexInGroup++;
        }


        public static void BeginToggleGroup (int selectedIndex) {
            m_selectedToggleIndexInGroup = selectedIndex;
        }


        public static int EndToggleGroup () {
            m_currentToggleIndexInGroup = 0;
            int selectedIndex = m_selectedToggleIndexInGroup;
            m_selectedToggleIndexInGroup = m_notStartedToggleIndexInGroup;
            return selectedIndex;
        }


        public static void Label (float x, float y, float width, float height, string label, GUIStyle style) {
            Label (new Rect (x, y, width, height), label, style);
        }


        public static void Label (Rect rect, string label, GUIStyle style) {
            GUI.Box (rect, label, style);
        }


        public static void PingObject (Rect rect, Object obj, Type type) {
            PingObject (rect, string.Empty, obj, type);
        }


        public static void PingObject (Rect rect, string label, Object obj, Type type) {
            float labelWidth = string.IsNullOrEmpty (label) ? 0 : textStyleLeft.CalculateTextWidth (label);
            Rect labelRect = new Rect (rect.x, rect.y, labelWidth, rect.height);
            float space = string.IsNullOrEmpty (label) ? 0 : 5;
            Rect objectRect = new Rect (labelRect.xMax + space, rect.y, rect.width - labelRect.width - space, rect.height);

            string objectText = obj == null ? "None (" + type.Name + ")" : obj.name;

            Label (labelRect, label, textStyleLeft);

            DrawSolidTexture (objectRect, pingFieldBackgroundColor.color);
            DrawSolidTexture (objectRect, pingFieldOutlineColor.color, new BorderWidth (1));
            Label (objectRect, objectText, textStyleLeft);

            if (Event.current.IsMouseDown (MouseButton.Any)) {
                if (Event.current.IsMouseInsideRect (objectRect)) {
                    UnityEditor.EditorGUIUtility.PingObject (obj);
                    Event.current.Use ();
                }
            }
        }


        public static void DrawSolidTexture (Rect rect, Color color) {
            DrawSolidTexture (rect, color, BorderWidth.zero, BorderRadius.zero);
        }


        public static void DrawSolidTexture (Rect rect, Color color, BorderWidth borderWidth) {
            DrawSolidTexture (rect, color, borderWidth, new BorderRadius (0));
        }


        public static void DrawSolidTexture (Rect rect, Color color, BorderRadius borderRadius) {
            DrawSolidTexture (rect, color, BorderWidth.zero, borderRadius);
        }


        public static void DrawSolidTexture (Rect rect, Color color, BorderWidth borderWidth, BorderRadius borderRadius) {
            DrawTexture (rect, solidWhite, ScaleMode.StretchToFill, defaultTextureAspect, color, defaultAlphaBlend, borderWidth, borderRadius);
        }


        public static void DrawTexture (Rect rect, Texture texture, ScaleMode scaleMode, Color color) {
            DrawTexture (rect, texture, scaleMode, defaultTextureAspect, color, defaultAlphaBlend, BorderWidth.zero, BorderRadius.zero);
        }


        public static void DrawTexture (Rect rect, Texture texture, ScaleMode scaleMode, float textureAspect, Color color, bool alphaBlend, BorderWidth borderWidth, BorderRadius borderRadius) {
            GUI.DrawTexture (rect, texture, scaleMode, alphaBlend, textureAspect, color, new Vector4 (borderWidth.left, borderWidth.top, borderWidth.right, borderWidth.bottom), new Vector4 (borderRadius.topLeft, borderRadius.topRight, borderRadius.bottomRight, borderRadius.bottomLeft));
        }


        internal static bool DrawEdge (Vector3 start, Vector3 startDirection, Vector3 end, Vector3 endDirection, Color startColor, Color endColor, Color startColorHighlighted, Color endColorHighlighted, float thickness = 3, Action onContextClick = null, bool highlightAndContextClick = true, float minDistanceFromStartEnd = 0) {
            int segments = 40;

            float dirFactor = Mathf.Clamp ((end - start).magnitude, 10f, 120);
            float correctionAngle = 22.5F;

            if (start.x > end.x) {
                if (start.y < end.y) {
                    if (Mathf.Approximately (startDirection.y, 0)) {
                        startDirection = (Quaternion.AngleAxis (-correctionAngle, new Vector3 (0, 0, 1)) * startDirection) * 3;
                    }

                    if (Mathf.Approximately (endDirection.y, 0)) {
                        endDirection = (Quaternion.AngleAxis (-correctionAngle, new Vector3 (0, 0, 1)) * endDirection) * 3;
                    }
                }
                else {
                    if (Mathf.Approximately (startDirection.y, 0)) {
                        startDirection = (Quaternion.AngleAxis (correctionAngle, new Vector3 (0, 0, 1)) * startDirection) * 3;
                    }

                    if (Mathf.Approximately (endDirection.y, 0)) {
                        endDirection = (Quaternion.AngleAxis (correctionAngle, new Vector3 (0, 0, 1)) * endDirection) * 3;
                    }
                }
            }


            Vector2 startTan = start + new Vector3 (startDirection.x, -startDirection.y) * dirFactor;
            Vector2 endTan = end + new Vector3 (endDirection.x, -endDirection.y) * dirFactor;


            Vector3[] points = Handles.MakeBezierPoints (start, end, startTan, endTan, segments);
            bool selectLine = HandleUtility.DistancePointBezier (Event.current.mousePosition, start, end, startTan, endTan) < thickness * 2 && highlightAndContextClick;

            Color oldColor = Handles.color;

            Handles.BeginGUI ();

            for (int i = 1; i < points.Length; i++) {
                float t = (i * 1F) / points.Length;

                float t1 = Math.Clamp01 (t, 0, 0.3F);
                float t2 = Math.Clamp01 (t, 0.7F, 1F);

                Color colorStart = selectLine ? startColorHighlighted : Color.Lerp (startColorHighlighted, startColor, t1);
                Color colorEnd = selectLine ? endColorHighlighted : Color.Lerp (endColor, endColorHighlighted, t2);
                Color color = Color.Lerp (colorStart, colorEnd, t);

                Handles.color = color;
                Handles.DrawAAPolyLine (thickness, points[i - 1], points[i]);
            }

            Handles.EndGUI ();

            Handles.color = oldColor;

            if (selectLine && Event.current.type == EventType.MouseDown && Event.current.button == 1) {
                // use sqr distance for better performance.
                float sqrDistanceFromStart = (Event.current.mousePosition - (Vector2) start).sqrMagnitude;
                float sqrDistanceFromEnd = (Event.current.mousePosition - (Vector2) end).sqrMagnitude;

                if (sqrDistanceFromStart > minDistanceFromStartEnd * minDistanceFromStartEnd && sqrDistanceFromEnd > minDistanceFromStartEnd * minDistanceFromStartEnd) {
                    if (onContextClick != null) {
                        onContextClick ();
                        Event.current.Use ();
                        return true;
                    }
                }
            }

            return selectLine;
        }


        internal static void DrawEdgeToMouse (EdgePoint startPoint, Color endColor, float thickness = 3) {
            Vector2 start = startPoint.point;
            Vector2 end = Event.current.mousePosition;

            Vector2 endDirection = (start - end).normalized;
            endDirection.y = -endDirection.y;

            DrawEdge (start, startPoint.direction, end, endDirection, startPoint.highlightedColor, endColor, startPoint.highlightedColor, endColor, thickness);
        }


        public static bool DrawEdge (EdgePoint startPoint, EdgePoint endPoint, float thickness = 3, Action onContextClick = null, bool highlightAndContextClick = true, float minDistanceFromStartEnd = 0) {
            return DrawEdge (startPoint.point, startPoint.direction, endPoint.point, endPoint.direction, startPoint.normalColor, endPoint.normalColor, startPoint.highlightedColor, endPoint.highlightedColor, thickness, onContextClick, highlightAndContextClick, minDistanceFromStartEnd);
        }


        /*public static ExposedParameter ExposedParameter<T> (string title, ExposedParameter exposedParameter, Node node) where T : UnityEngine.Object {
            return ExposedParameter (title, exposedParameter, typeof (T), node);
        }*/


        public static ExposedParameter ExposedParameter (string title, ExposedParameter exposedParameter, Type type, Node node) {
            if (node == null || node.controller == null) return exposedParameter;
            if (type != typeof (UnityEngine.Object) && !type.IsSubclassOf (typeof (UnityEngine.Object))) {
                Debug.LogError ("Cannot use exposed parameter with non UnityEngine.Object's");
                return exposedParameter;
            }

            int selectedIndex = 0;

            string[] displayedOptions = new string[node.controller.exposedParameters.Length + 1];
            displayedOptions[0] = "None (" + type.Name + ")";

            for (int i = 1; i < displayedOptions.Length; i++) {
                ExposedParameter parameter = node.controller.exposedParameters[i - 1];

                if (parameter.type == null) {
                    displayedOptions[i] = parameter.name;
                }
                else {
                    displayedOptions[i] = parameter.name + " (" + parameter.type.Name + ")";
                }

                if (exposedParameter == parameter) {
                    selectedIndex = i;
                }
            }

            int oldSelectedIndex = selectedIndex;
            selectedIndex = EditorGUILayout.Popup (title, selectedIndex, displayedOptions);

            ExposedParameter newSelectedParameter = (selectedIndex > 0) ? node.controller.exposedParameters[selectedIndex - 1] : null;
            ExposedParameter oldSelectedParameter = (oldSelectedIndex > 0) ? node.controller.exposedParameters[oldSelectedIndex - 1] : null;

            // do nothing if nothing changed.
            if (newSelectedParameter == oldSelectedParameter) {
                return exposedParameter;
            }

            // none select
            if (newSelectedParameter == null) {
                // unbind old parameter if it is not null
                if (oldSelectedParameter != null) {
                    oldSelectedParameter.Unbind (node);
                }

                EditorGUIUtility.RepaintEditorsOfType<AIBehaviourInspector> ();
                return null;
            }

            // check type if it's not equal ask the user if he would change the type.
            if (newSelectedParameter.type != null && newSelectedParameter.type != type) {
                // create modal window
                bool changeType = EditorUtility.DisplayDialog ("The selected parameter is already bounded.", "The selected parameter is used in other nodes with a different type.\nDo you won't to change the type and remove the parameter of the other nodes ?", "Yes", "No");

                if (changeType) {
                    Node[] nodes = newSelectedParameter.boundNodes;
                    newSelectedParameter.UnbindAll ();

                    // set old nodes exposed parameters per reflection to null.
                    foreach (Node n in nodes) {
                        FieldInfo[] fields = n.GetFields<ExposedParameter> ();

                        foreach (FieldInfo field in fields) {
                            ExposedParameter parameter = field.GetValue (n) as ExposedParameter;
                            if (parameter && parameter == newSelectedParameter) {
                                field.SetValue (n, null);
                            }
                        }
                    }
                }
                else {
                    return exposedParameter;
                }
            }


            if (oldSelectedParameter) {
                oldSelectedParameter.Unbind (node);
            }

            newSelectedParameter.Bind (node, type);

            EditorGUIUtility.RepaintEditorsOfType<AIBehaviourInspector> ();

            return newSelectedParameter;
        }


        /*public static NodeParameter NodeParameter<T> (string title, ref NodeParameter nodeParameter, Node node) {
            return NodeParameter (title, nodeParameter, typeof (T), node);
        }*/


        public static NodeParameter NodeParameter (string title, NodeParameter nodeParameter, Type type, Node node) {
            if (node == null || node.controller == null) return nodeParameter;

            int selectedIndex = 0;
            string[] displayedOptions = new string[node.controller.nodeParameters.Length + 1];
            displayedOptions[0] = "None (" + type.Name + ")";

            for (int i = 1; i < displayedOptions.Length; i++) {
                NodeParameter parameter = node.controller.nodeParameters[i - 1];

                if (parameter.type == null) {
                    displayedOptions[i] = parameter.name;
                }
                else {
                    displayedOptions[i] = parameter.name + " (" + parameter.type.Name + ")";
                }

                if (nodeParameter == parameter) {
                    selectedIndex = i;
                }
            }

            int oldSelectedIndex = selectedIndex;
            selectedIndex = EditorGUILayout.Popup (title, selectedIndex, displayedOptions);

            NodeParameter newSelectedParameter = (selectedIndex > 0) ? node.controller.nodeParameters[selectedIndex - 1] : null;
            NodeParameter oldSelectedParameter = (oldSelectedIndex > 0) ? node.controller.nodeParameters[oldSelectedIndex - 1] : null;

            // do nothing if nothing changed.
            if (newSelectedParameter == oldSelectedParameter) {
                return nodeParameter;
            }

            // none select
            if (newSelectedParameter == null) {
                // unbind old parameter if it is not null
                if (oldSelectedParameter != null) {
                    oldSelectedParameter.Unbind (node);
                }

                return null;
            }

            // check type if it's not equal ask the user if he would change the type.
            if (newSelectedParameter.type != null && newSelectedParameter.type != type) {
                // create modal window
                bool changeType = EditorUtility.DisplayDialog ("The selected parameter is already bounded.", "The selected parameter is used in other nodes with a different type.\nDo you won't to change the type and remove the parameter of the other nodes ?", "Yes", "No");

                if (changeType) {
                    Node[] nodes = newSelectedParameter.boundNodes;
                    newSelectedParameter.UnbindAll ();

                    // set old nodes exposed parameters per reflection to null.
                    foreach (Node n in nodes) {
                        FieldInfo[] fields = n.GetFields<NodeParameter> ();

                        foreach (FieldInfo field in fields) {
                            NodeParameter parameter = field.GetValue (n) as NodeParameter;
                            if (parameter && parameter == newSelectedParameter) {
                                field.SetValue (n, null);
                            }
                        }
                    }
                }
                else {
                    return nodeParameter;
                }
            }


            if (oldSelectedParameter) {
                oldSelectedParameter.Unbind (node);
            }

            newSelectedParameter.Bind (node, type);
            return newSelectedParameter;
        }

    }


    public enum LabelPosition {

        None,
        Left,
        Top,
        Right,
        Bottom

    }

    public enum Alignment {

        TopLeft,
        TopRight,
        BottomLeft,
        BottomRight,
        Center,
        MiddleLeft,
        MiddleRight,
        MiddleTop,
        MiddleBottom

    }


}
#endif